﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
using SZL= ICSharpCode.SharpZipLib.Zip;
using System.IO;

namespace PowerShellZip.Commands
{
    [Cmdlet(VerbsData.Import,"Zip")]
    public class ImportZip : CmdletBase
    {
        private Dictionary<string, SZL.ZipFile> zipFiles;

        private string[] targetDirectoryParam;
        [Parameter(Position = 0,Mandatory=false)]
        [ValidateNotNullOrEmpty]
        public string[] TargetDir
        {
            get { return targetDirectoryParam; }
            set { targetDirectoryParam = value; }
        }

        private string[] zipFilePathParam;
        [Parameter(Position = 1, Mandatory = false)]
        [ValidateNotNullOrEmpty]
        public string[] ZipFile
        {
            get { return zipFilePathParam; }
            set { zipFilePathParam = value; }
        }

        private string[] entryRootPathParam;
        [Parameter(Mandatory = false)]
        [ValidateNotNullOrEmpty]
        public string[] EntryRoot
        {
            get { return entryRootPathParam; }
            set { entryRootPathParam = value; }
        }

        private PSObject[] pipelineObjectParam = null;
        [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = false)]
        [ValidateNotNullOrEmpty]
        public PSObject[] PipelineObject
        {
            get { return pipelineObjectParam; }
            set { pipelineObjectParam = value; }
        }

        protected override void BeginProcessing()
        {
            zipFiles = new Dictionary<string, SZL.ZipFile>();
        }

        protected override void ProcessRecord()
        {
            string targetDirPath = null; // Directory where zip file should be stored
            string zipFilePath = null; // Path to the Zip File 
            string entryRootPath = "";  // Path to Zip entries that is stripped away (Entries aren't in this path are skipped)



            // Set Paths from command parameters
            if (TargetDir != null && TargetDir.Length > 0)
                targetDirPath = Path.Combine(this.SessionState.Path.CurrentLocation.ToString(), TargetDir[0]);

            if (ZipFile != null && ZipFile.Length > 0)
                zipFilePath = Path.Combine(this.SessionState.Path.CurrentLocation.ToString(), ZipFile[0]);

            if (EntryRoot != null && EntryRoot.Length > 0)
                entryRootPath = EntryRoot[0];

            

            if (PipelineObject != null)
            {
            }

            if (zipFilePath != null)
            {

                SZL.ZipFile zipFile = null;
                if (!zipFiles.TryGetValue(zipFilePath.ToLower(), out zipFile))
                {
                    FileInfo zipFileInfo = new FileInfo(zipFilePath);

                    if ( zipFileInfo.Exists )
                    {
                        zipFile = new SZL.ZipFile(zipFilePath);

                        zipFile.NameTransform = new SZL.ZipNameTransform();
                        zipFiles.Add(zipFilePath.ToLower(), zipFile);
                    }
                }
                if (zipFile != null)
                {
                    foreach (SZL.ZipEntry entry in zipFile)
                    {
                        string directorySeparator = new string(Path.DirectorySeparatorChar,1);
                        string entryName = entry.Name;
                        entryName = entryName.Replace('/', Path.DirectorySeparatorChar);
                        entryRootPath = entryRootPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);

                        if (!entryRootPath.EndsWith(directorySeparator))
                            entryRootPath += directorySeparator;

                        if (entryRootPath.StartsWith(directorySeparator))
                            entryRootPath = entryRootPath.Substring(1);


                        if (entryName.ToLower().StartsWith(entryRootPath.ToLower()))
                        {
                            entryName = entryName.Substring(entryRootPath.Length);
                            string entryFileName = Path.Combine(targetDirPath, entryName);
                            FileInfo entryFileInfo = new FileInfo(entryFileName);
                            DirectoryInfo entryDirectory = entryFileInfo.Directory;
                            if (!entryDirectory.Exists)
                                entryDirectory.Create();

                            using (Stream inputStream = zipFile.GetInputStream(entry))
                            {
                                using (FileStream outputStream = new FileStream(entryFileInfo.ToString(), FileMode.Create))
                                {

                                    byte[] buffer = new byte[8192];
                                    int offset = 0;
                                    int bytesRead = 0;

                                    do
                                    {
                                        bytesRead = inputStream.Read(buffer, offset, buffer.Length);
                                        outputStream.Write(buffer, offset, bytesRead);

                                    } while (bytesRead > 0);

                                    outputStream.Close();
                                }
                                inputStream.Close();
                            }
                        }
                        
                    }
                }
            }
            else
            {
                // TODO: Implement Error Handling
            }
            
        }

        protected override void EndProcessing()
        {
            foreach (KeyValuePair<string,SZL.ZipFile> zipFile in zipFiles)
            {
                zipFile.Value.Close();
                FileInfo fileInfo = new FileInfo(zipFile.Key);
                this.WriteObject(fileInfo);
            }

            zipFiles = null;
        }

        protected override void StopProcessing()
        {
            foreach (KeyValuePair<string, SZL.ZipFile> zipFile in zipFiles)
            {
                zipFile.Value.Close();
            }
            zipFiles = null;
        }
    }
}
